package org.NooLab.glue.filter;


  
import java.util.Map;
import java.util.SortedMap;

import org.NooLab.utilities.files.DFutils;
import org.NooLab.utilities.logging.PrintLog;
import org.NooLab.utilities.net.GUID;
import org.NooLab.utilities.objects.StringedObjects;
import org.NooLab.utilities.strings.StringsUtil;

 
import org.NooLab.glue.components.MessageBoardFactory;
import org.NooLab.glue.components.MessageBoardFactoryProperties;
import org.NooLab.glue.components.OpenTransactions;
import org.NooLab.glue.components.ParticipantsUpLinkConnectorIntf;
import org.NooLab.glue.components.SenderConfirmationIntf;
import org.NooLab.glue.components.SenderWrap;
import org.NooLab.glue.components.pkg.TaskPackage;
import org.NooLab.glue.instances.ParticipantsSettings;
import org.NooLab.glue.interfaces.ReceptorDigestIntf;
import org.NooLab.glue.msgframe.BasicMessageParams;
import org.NooLab.glue.msgframe.CtxtXMessage;
import org.NooLab.glue.msgframe.DataDeliveryParamsPlus;
import org.NooLab.glue.msgframe.XMessage;
import org.NooLab.glue.msgframe.XRegistration;
import org.NooLab.glue.net.ComBox;
import org.NooLab.glue.net.env.OpenDataDeliveries;
import org.NooLab.glue.net.env.OpenDataDelivery;
import org.NooLab.glue.storage.MsgBoardDataStorage;
import org.NooLab.glue.subscription.FilterPackageIntf;
import org.NooLab.glue.subscription.context.Context;
import org.NooLab.glue.subscription.context.ContextInfra;
import org.NooLab.glue.transaction.Transaction;
import org.NooLab.glue.transaction.Transactions;


/*
variant 1: ID for opened transaction, this ID is unique across all interactions with 
           the MessageBoard for all of the instances
           
<messageboard name="spela" role="SOURCE">
	<transaction>
		  <id instance="267" value="2"/>
		  <name value="test-source-1-85e949cc-de3d-48a7-996a-1475369e30e4"/>
	</transaction>
</messageboard>


		
variant 2:
<messageboard name="spela" role="SOURCE">
	<request type="delivery">
			
			<transactionID value=""/>
			<instance id="234"/>
	</request>
</messageboard>



variant 3: notification about results
	<messageboard name="spela" role="BOARD">
	   <transaction>
	      <transactionID value="259"/>
	      <issuer instanceID="1569" role="SOURCE"/>
	      <notification type="results"/>
	   </transaction>
	</messageboard>

 */


/**
 * 
 * same approach as in SourceMessageInterpreter{}
 * 
 * This class organizes the replies from the SOURCE instance to the MessageBoard  
 * 
 * 
 * TODO  for restlet transport, we have everywhere to add this (or sth similar!!) 
 *   
 *       xmlRestPrefix = xMessage.createRestletRoutingPrefix("", "/data", "post");
 *       
 * 
 */
public class SourceBoardRepliesInterpreter {

	private static final String ContextInfra = null;
	// =================================

	// object references ..............
	
	MessageBoardFactory mbf;
	//MessageBoard messageBoard;
	TaskPackage taskPackage;
	
	ComBox comBox ;
	
	// GlobalReceipts globalReceipts;
 	
	Transactions transactions;
	MsgBoardDataStorage msgBoardStorage;
	
	ReceptorDigestIntf resultsReceiver; // callback into the "MessageBoardParticipant{}"
	SenderConfirmationIntf informSender;
	ParticipantsUpLinkConnectorIntf  upLinkProcedures ;
	
	CtxtXMessage cxmsg;
	SubscriptionHandlerInterpreter subscriptionHandler;
	
	ContextInfra contextInfra;
	FilterPackageIntf filter;
	XRegistration xRegistration ;
	
	// main variables / properties ....
	
	boolean filterIsActivated;
	String nameOfInstance = "";
	String privateTransactionIdentifier = "" ;
	
	
	boolean transmitResourceAsReference=false ;
	
	
	
	
	// volatile variables .............
	
	String contentType="" ;
	int msgBoardPort=0;
	
	int protocolID;
	
	Map<String, Object> mappedPackagesCollection;
	
	
	XMessage xmsg ;
	SenderWrap sender;
	
	/** a filename, URL, etc. any kind of "address"   */
	String resourceLocator = "";

	
	
	// helper objects .................
	
	StringedObjects stringedObj = new StringedObjects();
	
	PrintLog out;
	DFutils fileutil = new DFutils();  
	StringsUtil strgutil = new StringsUtil();
	
	 
	// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
	public SourceBoardRepliesInterpreter( MessageBoardFactory mbf, 
										  XRegistration xreg, 
										  ReceptorDigestIntf resultsreceiver,
										  SenderConfirmationIntf informsender,
										  TaskPackage taskPackage,
										  FilterPackageIntf filter,
										  String nameofinstance,
									 	  PrintLog outprn){
		
		this.mbf = mbf ;
		
		nameOfInstance = nameofinstance;
		
		// globalReceipts = messageBoard.getGlobalReceipts();
		
		msgBoardStorage = mbf.getMessageBoardStorage() ;
		
		xRegistration = xreg;
		transactions = this.mbf.getTransactions() ;
		
		resultsReceiver = resultsreceiver;
		this.taskPackage = taskPackage;
		
		informSender = informsender;
		
		comBox = mbf.getComBoxFactory().getComBox() ;
		
		protocolID = mbf.getComBoxFactory().getProtocolID();
		
		out = outprn;
		
		upLinkProcedures =  (ParticipantsUpLinkConnectorIntf) mbf.getParticipantReference().getUpLinkProcedures();
	 	
		contextInfra = new ContextInfra(out);
	
		cxmsg = new CtxtXMessage(out);
		subscriptionHandler = new SubscriptionHandlerInterpreter( mbf, null, outprn);
		
		
		msgBoardPort = xRegistration.getActiveport() ;
		privateTransactionIdentifier = GUID.uniquevalue() ;
	}
	// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
	
	
	/**
	 * 
	 * TODO:  notification sent by Board about data which are delayed, or have been dropped ...
	 * 
	 */
	public int interpretingSingleReplyMessage( String rawXmlStr ){
		int result = -1;
	
		boolean hb ;
		String roleOfSender ;
		
		String[] rawIndicators, rawOptionalIndicators ;
	  
		
		
		xmsg = new XMessage(out) ;
		
		// <messageboard name="spela" role="BOARD">
		roleOfSender = xmsg.getSpecifiedInfo(rawXmlStr,"//messageboard","role").toLowerCase() ;  

		if ((roleOfSender!=null) ){
			
			// hb = (roleOfSender.toLowerCase().contains("source") );
			hb = (MessageBoardFactoryProperties.isSourceType( roleOfSender ));
			if (hb==false){
				hb = (roleOfSender.toLowerCase().contains("board") );
			}
		
			if (hb==false){
				if (protocolID >= MessageBoardFactoryProperties.protocolTCP){
					hb = true;
				}else{
												out.printErr(2, "message acceptance denied, reason: role does not match.");
												out.printErr(2, "message was: \n"+rawXmlStr );
				    return 0; // 0 = suppressing error messages... in httpc mode, some messages for the receptor could arrive here...
				}
			}
		}
		
		try{

			// we first test whether we have an in-Session response 
			rawOptionalIndicators = new String[]{this.nameOfInstance, "<response" , "<resource value=", "<return to=", "<instanceID value" } ;
			
			if ( rawOptionalIndicatorsCheck( rawXmlStr, rawOptionalIndicators,1) == false){
			
				rawOptionalIndicators = new String[]{this.nameOfInstance, "transaction id=\"" , "transactionID", "<name value=\"*\"" } ;
			
				if ( rawOptionalIndicatorsCheck( rawXmlStr, rawOptionalIndicators,1) == false){
					return -402;
				
				}
			}
			result =0;
			
			rawIndicators = new String[]{"<transaction>","<id "} ;
			if (replyVariant_DataDeliveryOnOpenedTransaction( xmsg, rawXmlStr , rawIndicators) ){ return result; }
			
			
			rawIndicators = new String[]{"<transaction>","<confirmation", "type=\"taskdata\"", "<transaction id="} ;
			if (replyVariant_ConfirmationForDeliveryofData( xmsg, rawXmlStr , rawIndicators) ){ return result; }
			
			rawIndicators = new String[]{ "<transaction>", "<request ","type=\"subscription\"" ,"<context"} ;
			if (replyVariant_ConfirmationForSubscriptionDelivery( xmsg, rawXmlStr ,rawIndicators) ){ return result; } 

			if (roleOfSender.contentEquals("board")){
				// if "BOARD" then it is a message sent by the board, hence we are in the SOURCE

				rawIndicators = new String[]{"<notification ", "type=\"results\"", "<issuer"} ;
				if (replyVariant_NotificationAboutResults( xmsg, rawXmlStr , rawIndicators) ){ return result; }
				
				rawIndicators = new String[]{"<notification ", "type=\"state\"", "secret=\""} ;
				if (replyVariant_NotificationAboutTransaction( xmsg, rawXmlStr , rawIndicators) ){ return result; }
				
				rawIndicators = new String[]{"<register","<notification ", "type=\"connection\"" } ;
				if (handling_NotificationAboutConfirmedConnection( xmsg, rawXmlStr , rawIndicators) ){ return result; }
			
				rawIndicators = new String[]{"<state","<instanceID ", "code=\"", "reason=\"" } ;
				if (handling_deniedTransaction( xmsg, rawXmlStr , rawIndicators) ){ return result; }

				rawIndicators = new String[]{"<transaction>","<return", "type=\"meta\"", "idlist="} ;
				if (handling_requestedTransactionList( xmsg, rawXmlStr , rawIndicators) ){ return result; }

				
			}
 			
			if (roleOfSender.contentEquals("receptor")){  // should not happen ...
				out.print(5,"before <replyVariant_DigestingNotification)>\n"+ rawXmlStr);
				
				rawIndicators = new String[]{"<transaction>", "<notification ", "type=\"state\""} ;
				if (handling_DigestingNotification( xmsg, rawXmlStr , rawIndicators) ){ return result; }
				// these are notifications OTHER than notification about results
				// actually, no reply is scheduled upon receiving it
			}

			// from RESTful services we will get a resource string, which we can use to create a get statement 
			rawIndicators = new String[]{"<return ", "type=\"resource\"", "<transaction"," id=", "secret=", "<resource "," address=" } ;
			// if ( replyVariant_GetResultsAsResource( xmsg, rawXmlStr , rawIndicators )){ return result; }
			
			// for delivery of results, roleOfSender = board
			rawIndicators = new String[]{"<return ", "type=\"results\"", "<transactionID", "<result>"} ;
			if (replyVariant_ConfirmArrivalOfResults( xmsg, rawXmlStr , rawIndicators) ){ return result; }
			
			rawIndicators = new String[]{"<return ", "type=\"results\"", "<transactionID", "<result", "state=\"error"} ;
			if (replyVariant_HandlingErrorMsgOnResultsRequest( xmsg, rawXmlStr , rawIndicators) ){ return result; }
 
			
			rawIndicators = new String[]{"<return ", "type=\"state\"", "<transactionID"} ; 
			if (replyVariant_HandlingStateMessage( xmsg, rawXmlStr , rawIndicators) ){ return result; }
			
			rawIndicators = new String[]{"<control ", "type=\"cancel","<transaction"," id=\"", "<task"} ; //  
			if (handling_NotificationAboutControlTask( xmsg, rawXmlStr , rawIndicators) ){ return result; }
			
			rawIndicators = new String[]{"<control ", "type=\"cancel","<transaction"," id=\"", "success"} ; //  
			if (handling_ConfirmationAboutCancelTask( xmsg, rawXmlStr , rawIndicators) ){ return result; }
			
			/*
			 * e("transaction")
					.e("transaction").a("id", ""+basics.transactionID).up()
					.e("confirmation").a("type", subject ).a("secret", basics.privateTransactionIdentifier ).up()
					.e("result").a("success", listedIDs).a("unknown", notTreatedRqTids)	.up() 
					.e("instance").a("id", ""+ basics.instanceID ).up() 
					
			 */
			rawIndicators = new String[]{"<instance ", "<transaction"," id=\"", "type=\"task::"} ; //  "type=\"cancel\"",
			if (handling_ConfirmationAboutTaskRequest( xmsg, rawXmlStr , rawIndicators) ){ return result; }
			
			
			result = -3;
			
		}catch(Exception e){
			e.printStackTrace();
		}
		
		
		return result;
	}
	
	
	public boolean relayInSessionResponseToDigester( ReceptorDigestIntf resultsReceiver, String rawXmlMsg){
	
		boolean result=false;
		
		long transactionID;
		String roleOfSender, encodedPackage ;
		String[] rawIndicators;
		
		TaskPackage taskPackage;
		Transaction mirrTransaction;
		
		
		
		xmsg = new XMessage(out) ;
		
		try{
			
			// <messageboard name="spela" role="BOARD">
			roleOfSender = xmsg.getSpecifiedInfo(rawXmlMsg,"//messageboard","role").toLowerCase() ;
			
			if ((roleOfSender==null) || (roleOfSender.toLowerCase().contains("iboard") )){
				result = true;
				// that's just to protect the sources from similar iboard messages
				return result;
			}
			
			
			if ((roleOfSender==null) || (roleOfSender.toLowerCase().contains("source") )){
				
				rawIndicators = new String[]{"<transaction>", "<data", "<transactionID value=", "<resource ","<values>"} ;
				result = rawIndicatorsCheck( rawXmlMsg, rawIndicators);
			
				if (result){

					mirrTransaction = xmsg.readReceptorTransaction(rawXmlMsg);
					mirrTransaction.setState(Transaction.TS_DATA_ACCEPTING);

					transactionID = mirrTransaction.getTransactionID();

					// sth like "rO0ABXNyACpvcmcuTm9vTGFiLmdsdWUuY29tcG9uZW50cy5wa2cuVGFza1BhY2thZ2Xw ..."
					encodedPackage = (String) xmsg.readTransactionDataDelivery(rawXmlMsg);

					// decode it
					taskPackage = (TaskPackage) stringedObj .decode(encodedPackage);

					// we have to create a mirror of the transaction

					taskPackage.setTransactionMirror(mirrTransaction);
					taskPackage.setTransactionID(transactionID);

					resultsReceiver.performTask(taskPackage);

				} // rawIndicatorsCheck ok?
				
			}else{
				return false; // 0 = suppressing error messages... in httpc mode, some messages for the receptor could arrive here... 
			}
			
		}catch(Exception e){
			e.printStackTrace();
		}
		
		return result;
	
	}

	
	/**
	 * after requesting the transaction ID, and receiving the transaction ID, 
	 * here, we will send data to the MessageBoard and creating a mirror of the transaction
	 * 
	 * 
	 * @param xmsg
	 * @param rawXmlStr
	 * @param rawIndicators
	 * @return
	 */
	private boolean replyVariant_DataDeliveryOnOpenedTransaction( XMessage xMessage, String rawXmlStr , String[]rawIndicators){
		
		boolean result = false,hb;
		String encodedTaskPackage, xmlstr , str,startupSecret,
			   bufferfilename,xmlRestPrefix="" ,contextXmlSection ,
			   encodedEligibilityObj, eligibilitySection,replacementStr	;
		
		int dataType=-1, tx;
		long transactionID,tid, timeofdelivery, ageOfExpiry=0 ;
		
		Context context = null ;
		Transaction mirrTransaction ;
		
		
			result = rawIndicatorsCheck( rawXmlStr, rawIndicators);
			
			if (result){
				result = false;
				
				
				//p1 = rawXmlStr.indexOf("<transaction>");  p2 = rawXmlStr.indexOf("<id ");
				
				 
																out.print(4, "handlingTransmissionReplies(1) ... ");
					// retrieving our transaction ID
					mirrTransaction = xMessage.readConfirmedTransactionReceipt(rawXmlStr) ;
					transactionID = mirrTransaction.getTransactionID() ;
					
					
					startupSecret = xMessage.getSpecifiedInfo(rawXmlStr, "//transaction/id", "secret");
					
					
					if (mappedPackagesCollection.containsKey(startupSecret)){
						taskPackage = (TaskPackage) mappedPackagesCollection.get(startupSecret) ; 
						mappedPackagesCollection.remove(startupSecret) ;
					}
					// else : we take just the last available one, which in most most cases is without problems 
					  
					
					if (taskPackage==null){
						// - cancel the transaction on the MessageBoard;
						// - issue an error message here (Exception?)
						return true;
					}
					if (taskPackage.getContext()!=null){
						context = taskPackage.getContext();
						// contains the information for matching this message to subscriptions
					}
					// 
					
																out.print(2,"confirmed transaction-ID for instance <"+ mirrTransaction.getInstanceID() +"> : " + transactionID) ;
			        //
				    //  now we create the data delivery:	
			        if (transmitResourceAsReference){
			        	dataType = Transaction.DT_REF ;
			        }else{
			        	dataType = Transaction.DT_DAT ;
			        }
			        
			        encodedTaskPackage = stringedObj.encode( taskPackage ) ;
			        
	            	// ageOfExpiry : if 0 -> no automatic expiry, if >0 message/transaction may expire while waiting for a RECEPTOR
			        BasicMessageParams basics = new BasicMessageParams();
					
					basics.transactionID = transactionID;
					basics.instanceID = 0;
					basics.instanceName = nameOfInstance;
					basics.privateTransactionIdentifier = privateTransactionIdentifier;
					basics.roleOfSender = "SOURCE" ;
					
					DataDeliveryParamsPlus paramsplus = new DataDeliveryParamsPlus();
					
					paramsplus.ageOfExpiry = ageOfExpiry ;
					paramsplus.contentType = contentType ;
					paramsplus.dataType = dataType ;
					paramsplus.filterIncluded = (filter!=null) && (filterIsActivated) ;
					paramsplus.linkRelation =  xRegistration.getSrlinkRelation() ;
					paramsplus.relationItemID = xRegistration.getSrRelationItemID()  ;
					paramsplus.relationGroupID = xRegistration.getSrRelationGroupID()  ;
					
					// generalTaskRelayFlag
					ParticipantsSettings ps = mbf.getFactoryProperties().getParticipantFactory().config() ;
					paramsplus.taskRelayFlag = ps.getGeneralTaskRelayFlag();
					paramsplus.relayHopsLimit = ps.getTaskRelayHopsLimit() ;
					
					// should relaying be performed only if context matches ?
					if (context!=null){
						paramsplus.relayByContext = context.getClientRelayByContext();
					}
					
					
			        mirrTransaction.setType( dataType ) ;
			        xmlstr = xMessage.createDataDeliveryMessageFrame( basics, // nameOfInstance, "SOURCE", transactionID, privateTransactionIdentifier, 
			        												  paramsplus // ageOfExpiry, contentType, dataType, (filter!=null) && (filterIsActivated), xRegistration.getSrlinkRelation(), xRegistration.getSrRelationItemID(),xRegistration.getSrRelationGroupID() 
			        												  ) ;
						
			        
			        if (context!=null){
			        	// create an xml section <context> <...> </context> and insert it into <transaction> ...</transaction>
			        	
			        	contextXmlSection = contextInfra.prepareXmlForMessageContext( basics, context ) ;
			        							out.print(4, "preparing contextXmlSection, len = "+contextXmlSection.length()+" ...");
			        	xmlstr = xMessage.insertXmlStrToXmlStr( xmlstr, "transaction", contextXmlSection) ;
			        	
			        			 if (xmlstr.contains("<context")==false){
			        				 out.printErr(2, "\ntag '<context>' expected, but not found in prepared xml ! \n");
			        			 }else{
			        				 out.print(4, "\ntag '<context>' expected (a) and found...\n");
			        			 }
			        }
			        
			        /*
			         * eligibility is just an extract from taskPackage, which allows 
			         * - for faster matching of source and receptor
			         * - for a more detailed communication between participants
			         */
			        if (taskPackage.getEligibilityForReceptors() != null){
			        	encodedEligibilityObj = stringedObj.encode( taskPackage.getEligibilityForReceptors() ) ;
			        	eligibilitySection ="<eligibility>" + encodedEligibilityObj + "\n</eligibility>\n          ";
			        }else{
			        	eligibilitySection = "";
			        }
			        // this xml now contains a comment tag containing ##DATA##
			        // this we have to replace with the real data, either the reference, or the encoded data as str
			        if (dataType==1){
			        	replacementStr = "<resourcelocator>"+resourceLocator+"</resourcelocator>" ;
			        }else{
			        	replacementStr = "<format zip=\"0\" encoding=\"base64\"/>\n" + 
			        					 "         <values>"+encodedTaskPackage+"</values>\n" ;
			        				
			        }
			        replacementStr = eligibilitySection+replacementStr; 
			        
			        
			        xmlstr = introduceDatatoMsgFrame( xmlstr, replacementStr) ;
			        										out.print(4,"instance <"+ mirrTransaction.getInstanceID() +"> has prepared the data for transaction-ID : " + transactionID) ;
			        			
			        mirrTransaction.setDataObject(encodedTaskPackage ) ;
			        
		            if ((filter!=null) && (filterIsActivated)){
		            	replacementStr = "" ;
		            	xmlstr = introduceFiltertoMsgFrame( xmlstr, replacementStr) ;
		            }
		            
		            
		            hb = true;
		            if (xRegistration.isTransactionPersistence()==true){ 
		            	// saving the list of open transactions needs a filename which indicates the
		            	// type of instance (B;S;R) , in order to avoid confusion when running on a single machine
		            	msgBoardStorage = mbf.getMessageBoardStorage() ;
		            	
		            	if (msgBoardStorage != null) {
		            		
		            		str = xRegistration.getNameOfApplicant() ;
		            		
		            		 
		            		// upon receiving results, this must be removed from this collection again
		            		mbf.getOpenTransactions().put(transactionID, privateTransactionIdentifier) ; 
		            		
		            		msgBoardStorage.saveParticipantstransactions( str,"SOURCE", "open", mbf.getOpenTransactions() ); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

		            		// the map of open transactions is of SortedMap<Long, String> , like
		            		// D:/dev/java/data/test/mb/transferBuffer/testsource0-source-1-7132c490-f138-11e0-84ce-e0f847024516/~tstor-open_so.dict
		            		// where the key is the transaction id, and the second the secret, only known by the issuer 
		            		// of the transaction and this map (security)
		            		// data look like [25=241feb27-0581-4ca2-93d0-ee950f646178] 
		            		
		            		// MessageBoard...
		            		// the parent directory (D:/dev/java/data/test/mb/transferBuffer/) then contains a file
		            		// like  "~rn-25-so.btmp", which is a stored Notification ???
		            		
		            		mbf.getOpenDataDeliveries().addItem( transactionID, privateTransactionIdentifier, rawXmlStr, 0) ;
		            		
		            	}else{
			            	
			            	if (msgBoardStorage == null){
			            		result = false ;
			            		out.print(2,"Transaction persistence requested, but storage is unknown, hence, nothing will be sent, transaction has been interrupted!");
			            	}
			            }
		            }
		            if (hb){
		            	// adding meta info about Restlet routes as xml prolog...
		            	
		            	if (mbf.getFactoryProperties().getProtocolId() <= MessageBoardFactoryProperties.protocolFTP){
		            		xmlRestPrefix = xMessage.createRestletRoutingPrefix("", "/data", "post");
		            		xmlstr = xmlRestPrefix + xmlstr;
		            	}
		            							out.print(4,"\n{class SourceBoardRepliesInterpreter}, instance <"+ mirrTransaction.getInstanceID() +
		            									    "> is sending data for transaction-ID : " + transactionID) ;
		            							out.print(2,"instance <"+ mirrTransaction.getInstanceID() +"> is sending transaction <" + transactionID+">") ;
		            							if (context!=null){	
		            								if (xmlstr.contains("<context")==false){
		            									out.printErr(2, "\ntag '<context>' expected (b), but not found in prepared xml ! \n\n"+xmlstr+"\n\n");
		       			        			 		}else{
		       			        			 			out.print(4, "\ntag '<context>' expected (b) and found...");
		       			        			 		}
		            							}
		            							out.print(5, "replyVariant_DataDeliveryOnOpenedTransaction(), now sending using wrapped sender...");
		            							
		            	sender = new SenderWrap(comBox);
		            	int r=sender.sendingMessage( xmlstr , msgBoardPort ) ;
				         
		            	sender=null;
		            	result = true;
		            	
		            	// completing data as used previously for the XML
		            	mirrTransaction.setClientSecret(privateTransactionIdentifier) ;
		            	mirrTransaction.setContentType(contentType) ;
		            	mirrTransaction.setDatatype(dataType) ;
		            	mirrTransaction.setLinkRelation( xRegistration.getSrlinkRelation() );
		            	mirrTransaction.setRelationItemID( xRegistration.getSrRelationItemID() ) ;
		            	mirrTransaction.setRelationGroupID( xRegistration.getSrRelationGroupID() ) ;
		            	
		            	// .e("timedata").a("expiry", ""+ageOfExpiry).a("delivery", ""+System.currentTimeMillis())
		            	timeofdelivery = System.currentTimeMillis() ;
		            	
		            	xmlstr = xMessage.removePrologFromXml(xmlstr) ;
		            	str = xMessage.getSpecifiedInfo(xmlstr, "timedata", "delivery") ;
		            	
		            		  if ((str.length()>0) &&(strgutil.isNumericX(str))){
		            			  timeofdelivery = Long.parseLong(str) ;
		            		  }
		            	mirrTransaction.setTimeofDelivery( timeofdelivery);  
		            	
		            	// bufferfilename = msgBoardStorage.createTransactionBufferfileName( mirrTransaction, "SOURCE" ) ;
		            	bufferfilename = msgBoardStorage.saveTransactionResultsObj( mirrTransaction, "SOURCE");
		            							out.print(3,"transaction buffered to file: "+bufferfilename) ;
		            	
		            	tid = mirrTransaction.getTransactionID() ;
		            	tx = transactions.getIndexByTransactionID(tid, 1);
		            	if (tx<0){
		            		transactions.addItem(mirrTransaction);
		            	}
		            	
		            	xRegistration.setOpenRequestsForTransaction( xRegistration.getOpenRequestsForTransaction()-1 );
						xRegistration.setCurrentlyWaitingforTID(false) ;
						
												// out.print(4, "transactions object from mbf (SourceBoardRepliesInterpreter()): "+ transactions.toString() );
						  
		            	// we never can know whether in advance whether a data delivery will be successful,
		            	// thus we have to open a position in a list of delivered, yet unconfirmed deliveries,
		            	// which is closed after getting the confirmation (see: replyVariant_ConfirmDeliveryofData()) 
		            	// if we do not receive such, we have to resend
		            	
				        // now we need also an adhoc process with timeout, which checks for the
				        // arrival of the echo to this delivery 
		            	// this process removes the connection from the respective collection, if the
		            	// confirmation does not arrive
		            	// note that 
		            	//   - the MessageBoard responds immediately upon request, it the transfer
		            	//     would take a lot of time
		            	//   - the MessageBoard will send a denial message, if the data would not be complete
		            	
		            	 
				        // we have to save "privateTransactionIdentifier" into a map, since we
				        // will need it for checking the confirmation of data delivery
		            	
				        mbf.getOpenDataDeliveries().addItem( transactionID, privateTransactionIdentifier, xmlstr, msgBoardPort ) ;
				        // TODO: OpenDataDeliveries is running a process, which detects
				        //       items being expired, .. if it detects one, it calls a 
				        //       routine in MessageBoard (handlingLostDataDelivery())
 		            	
		            };
			} // result = true?
		
		return result;
	}

	// 
	private boolean replyVariant_ConfirmationForDeliveryofData( XMessage xMessage, String rawXmlStr , String[]rawIndicators){
		boolean result = false;
		
		int  ix, port=0;
		String transactionSecret="",bufferfilename ;
		long transactionID=-1;
		 
		OpenDataDeliveries openDataDeliveries ;
		OpenDataDelivery openDataDelivery ;
		Transaction mirrTransaction;
		
		result = rawIndicatorsCheck( rawXmlStr, rawIndicators);
		
		if (result){
			result = false;
			
			transactionID  = xMessage.getTransactionID(rawXmlStr) ;
 			// is it contained here ? it should not...
			ix = mbf.getOpenDataDeliveries().indexOfItem(transactionID, transactionSecret) ;
			
					out.print(3, "confirmation for transaction id="+transactionID+" has been assigned to a transaction, which is known to be opened...");
					
			openDataDeliveries = mbf.getOpenDataDeliveries();
			
			if (ix<0){
				// adding to the list of opened transactions
				openDataDelivery = new OpenDataDelivery();
				openDataDelivery.setOpenTime( System.currentTimeMillis()) ;
				
				// XMessage xMessage,
				
				transactionSecret = xMessage.getSpecifiedInfo(rawXmlStr, "confirmation", "secret") ;
			 
				// "port" ?? is actually not needed... except this SOURCE relates to several MessageBoards...
				// anyway, the transaction would known
				// actually, this "rawXmlStr" should contain the original delivery !!! 
				openDataDeliveries.addItem( transactionID, transactionSecret, rawXmlStr, port) ;
				

				ix = mbf.getOpenDataDeliveries().indexOfItem(transactionID, transactionSecret) ;
			}
			
			if (ix>=0){
				// update state -> we need this especially for the httpc transport option, -> polling the state
				// second issue: administration of delivered transactions, has to be controlled !
				
				openDataDelivery = openDataDeliveries.getItem(ix) ;
				// 
				// transactions should have been loaded if program has been restarted
				mirrTransaction = transactions.getTransactionByID(transactionID) ;
				bufferfilename = msgBoardStorage.createTransactionBufferfileName( mirrTransaction, "SOURCE" ) ;
				
				if (fileutil.fileexists(bufferfilename)){
					
					mirrTransaction.setState( Transaction.TS_TASK_CONFIRMED );
					// saving again
					msgBoardStorage.saveTransactionResultsObj( mirrTransaction, "SOURCE" ) ;
					
					// an independent process is now scanning for open transactions, which are
					// in confirmed state
				}
				
			}
				
				
			
			result = true;
			// yes, remove it, no -> wait, check connection and resend
		}
		
		return result;
	}
	
	private boolean replyVariant_ConfirmationForSubscriptionDelivery( XMessage xmsg, String rawXmlMsg, String[] rawIndicators){
		boolean result=false;
		
		long iid, tid;
		
		Context context;
		
		
		
		result = xmsg.rawIndicatorsCheck( rawXmlMsg, rawIndicators);

		if ( result ){
			iid = xmsg.getinstanceID(rawXmlMsg, "instance");
			 // we should use a Subscription handler, that is common to sources and receptors...
												out.print(2, "subscription response received by source (iid:"+iid+")...");
												out.print(2, rawXmlMsg+"\n-----------------\n");
		    tid = xmsg.getTransactionID(rawXmlMsg);
		    // note, it may or may not contain a transaction, data, name !
		    context = subscriptionHandler.rebuildContextFromXml( rawXmlMsg );
		    
		    // read context, 
		    
		    // read subscription info
		    // subscriptionHandler.  ...
		}
		
		return result;
	}

	private boolean handling_requestedTransactionList( XMessage xMessage, String rawXmlStr , String[]rawIndicators){
		boolean result = false;
		
		TaskPackage taskPackage;
		
		result = rawIndicatorsCheck( rawXmlStr, rawIndicators);
		
		if (result){
			out.print(2, "handling_requestedTransactionList(), digesting and preparing task");
			taskPackage = new TaskPackage();

			
			
			taskPackage.setTypeOfProceedings("results::list");
			resultsReceiver.performTask(taskPackage);
		}
		
		return result;
	}
	
	
	private boolean replyVariant_NotificationAboutResults( XMessage xMessage, String rawXmlStr , String[]rawIndicators){
		boolean result = false;
		
		int port;
		long transactionID, instanceID, iid;
		String str, ourRole, roleOfInstance, secret, host,xmlstr , requestCommand;
		boolean dropping=false;
		
																// out.print(3, "handling hint provided by BOARD: replyVariant_NotificationAboutResults(), checking signature...");
		result = rawIndicatorsCheck( rawXmlStr, rawIndicators);
		
		if (result){
			result = false;
															   	out.printErr(3, "replyVariant_NotificationAboutResults(), method signature for XML is OK!");
															   	out.print(4, rawXmlStr );

					XRegistration xreg = mbf.getRegisteredInstances().get(0);
																out.print(3, "handlingTransmissionReplies(3), notification ... ");
		            transactionID = 0 ;
				    // <transactionID value= "23872">
				    // <issuer role=roleOfIssuer instanceID="123">
		            transactionID = xMessage.getTransactionID(rawXmlStr) ;
		           
		            instanceID = xMessage.getinstanceIDfromIssuer(rawXmlStr ) ;
		            iid = xreg.getInstanceID() ;
		            
		            if (iid != instanceID){
		            	return false;
		            }
		            
		             
		            int ix = mbf.getTransactions().getIndexByState( Transaction.TS_RESULTS_RETURNED,0);
		            
		            if (ix>=0){
		            	// do nothing
		            	out.printErr(2, "repeated Notification about transaction (tid:"+transactionID+"), which we already got") ;
		            	return true;
		            }
		            
		            dropping = false;
					// proceed only, if this transaction is in the list of open transactions
					 
					str = mbf.getSecretOfTransaction(transactionID);
					
					// the list of open transactions could be 0=empty, if we do not apply persistence...
					// so, if this list remains empty after a restart, we accept it anyway...  yet, it would be better to
					// use persistence all the time...
					if ((str.length()==0) && (mbf.getOpenTransactions().size()>0)){
						// we do NOT send an invitation to provide results
						out.print(3, "incoming notification about available results on transaction (id:"+transactionID+") dropped: already known...");
						out.print(3, "      reason (4): transaction (req-id:"+transactionID+") not found in local catalog: drop-note returned!");
						out.print(4, "      "+rawXmlStr);
						/*
						 	<messageboard name="spela" role="BOARD">
    						 	<transaction>
       						 	<transactionID value="547"/>
       						 	<issuer instanceID="2528" role="SOURCE"/>  // this needs a secret issued by the board !!!
       						 	<notification type="results"  secret="sadlkfj-34d-3249u"/>
    						 	</transaction>
 						 	</messageboard>
						*/
						
						// see: wxyz
						
						String xmlDropNote = "";
						
						secret = xMessage.getSpecifiedInfo(rawXmlStr, "notification","secret");
						roleOfInstance = "SOURCE" ;
						
						xmlDropNote = xMessage.createTransactionAdviceBySource( transactionID, iid, roleOfInstance, secret , "results", "drop")  ;
						
						// or?? createNotificationReplyForResultCommand
						
						SenderWrap sender = new SenderWrap(comBox);
						int r=sender.sendingMessage(xmlDropNote, xreg);
						delay(200);
						dropping = true;
						// we should send the standard message to the MessageBoard... to stop notification about this
						
						sender=null;
						return true;
					}
					// proceed only if we did not accept a notification for this open transaction
					
					
					
		            
		            str = xMessage.getNotificationInfo(rawXmlStr,"type") ;
		            
		            roleOfInstance = xMessage.getSpecifiedInfo(rawXmlStr,"issuer","role") ;
		            ourRole = mbf.getRegisteredInstances().get(0).getRole();
		            
		            if ((str.toLowerCase().contains("result")) ){
		            // we will NOT respond to further notifications, if we did not get a response to the last one
		           
		            // then we proceed to request results from MessageBoard , which, on return,
		            // will be processed first by xMessage.readReturnedResults
		           
		             	 
						// n = mbf.transactions.getTransactions().size();

						 
						// given the transactionID we look it up... via the factory
						privateTransactionIdentifier = mbf.getSecretOfTransaction(transactionID);
						
						port = xRegistration.getClientListeningPort() ;
						host = xRegistration.getHostname() ;
							   // default : "localhost" ; 
						
						
						if (dropping==false){
							requestCommand = "get";
						}else{
							requestCommand = "stop";
						}
						
						BasicMessageParams basicparams = new BasicMessageParams();
						
						basicparams.transactionID = transactionID;
						basicparams.instanceID = instanceID;
						basicparams.privateTransactionIdentifier = privateTransactionIdentifier;
						basicparams.roleOfSender = roleOfInstance ;
						
						xmlstr = xMessage.createNotificationReplyForResultCommand( basicparams, // roleOfInstance,instanceID, transactionID, privateTransactionIdentifier, 
																   				   ourRole ,requestCommand,
																   				   port, host);
						
						// the notification reply is some kind of signaling "hey, here I am!" :)		

						 
													out.print(3,"upon notification: sending request for delivery of results to port "+msgBoardPort+"...");
													out.print(4, ">>>>>>>>\n"+xmlstr+ "\n<<<<<<<<");

													// sending back to the MessageBoard
													
	                    sender = new SenderWrap(comBox);
						sender.sendingMessage( xmlstr , msgBoardPort ) ;
											         
						sender=null;
						 
						result = true;
					}
		} 
		
		return result;
	} // handling notification about results
	
	/* 
	private boolean replyVariant_GetResultsAsResource( XMessage xMessage, String rawXmlStr , String[]rawIndicators){
		boolean result = false;
		
		int r;
		String str ,scretstr,mappedSecret ;
		long transactionID, instanceID ;
		boolean confirmationSent=false;
		
		SortedMap<Long, String> opentransactions;
		TaskPackage resultPackage ;
		
		
		result = rawIndicatorsCheck( rawXmlStr, rawIndicators);
		
		if (result){
			result = false;
			
		}
		return result;
	}
	*/
	
	
	private boolean replyVariant_ConfirmArrivalOfResults( XMessage xMessage, String rawXmlStr , String[]rawIndicators){
		boolean result = false;
		
		int r, transactState,ix, sv,esv;
		String str ,secretstr, secret, bufferfilename  ;
		long transactionID, instanceID ,v;
		boolean confirmationSent=false;
		Object obj ;
		Transaction transaction;
		SortedMap<Long, String> opentransactions;
		TaskPackage resultPackage ;
		
		
		result = rawIndicatorsCheck( rawXmlStr, rawIndicators);
		
		if (result){
			result = false;
			
				//<transactionID value="285" state="25" secret="c8769ae8-bd42-41bd-91e7-b59ecff96732"/>
				  
				// <transactionID code="-18" secret="f4aeb8d0-1968-11e1-b495-e0f847024516" state="26" value="728"/> 
				
				transactionID = xMessage.getTransactionID(rawXmlStr) ;
				instanceID = xMessage.getinstanceID( rawXmlStr, "instanceID") ;
				
				transaction = transactions.getTransactionByID(transactionID) ;
				
				if (transaction!=null){
					upLinkProcedures.pause(transactionID,1);
					
				}

				// proceed only, if this transaction is in the list of open transactions
				// this checks the collection openTransactions...
				secretstr = mbf.getSecretOfTransaction(transactionID);
				
				if (secretstr.length()<=3){
					// perhaps in standard transactions ?
					
					ix = transactions.getIndexByTransactionID( transactionID, 1) ;
					secretstr = transactions.getItem(ix).getClientSecret() ;
					// still not found, ...we do NOT send an invitation to provide results, yet a notification about the error status would be fine
					if (secretstr.length()<=3){
						out.printErr(3, "security breach, no secret exchanged, notification dropped (3) ... ");
						// TODO ... create a message to the board that this transaction could be dropped
						return true;
					}
				} 
				
												out.printErr(4, "replyVariant_ConfirmArrivalOfResults() for transaction tid="+transactionID) ;
												out.print(5, rawXmlStr);
													
				v = xMessage.getSpecifiedValue(rawXmlStr, "transactionID", "state") ; 
				transactState = (int)v;
				
				// the transaction could be delayed... then we should not throw an error, since there are no results contained in this case
				// MessageBoardFactoryProperties.
				
				if (transactState == Transaction.TS_TASK_DELAYED){
					
					out.print(3, "transaction (tid:"+transactionID+") has been indicated to be delayed...");
					// no confirmation to board is necessary
					return true;
				}
			
				
				secret = xMessage.getSpecifiedInfo(rawXmlStr, "transactionID", "secret") ;
				
				privateTransactionIdentifier = mbf.getSecretOfTransaction(transactionID); 
				
				// should be called by the factory first (e.g. on startup):  storage.getParticipantstransactions( name, "SOURCE", "open")
				if ((secret.contentEquals(privateTransactionIdentifier)==false) &&
					(secret.contentEquals(secretstr)==false)){
					out.printErr(2, "security breach, secrets do not match.");
					// the same as above... inform the mb that this transaction could be dropped
					return true;
				}
					// encoded result object is contained as text between tags <result>...</result>
				str = xMessage.readReturnedResults( rawXmlStr ) ;
				resultPackage = null;
				
				try{
					// 
					obj = stringedObj.decode(str);
					
					if (obj instanceof TaskPackage){
						resultPackage = (TaskPackage)obj;
					}
					if (obj instanceof String){
						str = (String)obj ;
						// if the return is not a task package but an ordinary string, it is an error/state message
						sv = (int)xMessage.getSpecifiedValue(rawXmlStr, "transactionID", "state") ;
						
						esv = (int)xMessage.getSpecifiedValue(rawXmlStr, "transactionID", "code") ;
						 
						// transaction = transactions.getTransactionByID(transactionID) ;
						
						transaction.setTransactState(transactState) ;
						
						if (esv != Transaction.TS_ERR_NO){
							prepareErrorPackageForClient( transaction, transactionID, sv, esv , str);
						}else{
							prepareStatePackageForClient( transaction, transactionID, sv, esv , str);
						}
						
						transactions.save() ;
						out.printErr(2, "\n----------------------------------------\n"+
										"return is not a result package in replyVariant_ConfirmArrivalOfResults()! \n"+rawXmlStr);
						r = confirmToMessageBoard( xMessage, transactionID, instanceID, Transaction.TS_CLOSED, secret, "SOURCE", -1);
						
						return true;
					}
					if (transaction==null){
						
						return true;
					}
					
					
												out.printErr(3, "result got from board for transaction "+transactionID) ;
					String ctyp = resultPackage.getContentType(); // should be "result"
					str = resultPackage.getTypeOfProceedings() ;
					if ((ctyp==null) || (str.contains("stop::"))){
						// transaction canceled
						// out.printErr(2, "transaction stopped, result package returned with "+str);
						
						if (resultPackage.getTransactionID()<=0){
							resultPackage.setTransactionID( transactionID);
						}
						
						//
						OpenDataDeliveries openDataDeliveries ;
						openDataDeliveries = mbf.getOpenDataDeliveries();
						ix = openDataDeliveries.indexOfItem(transactionID) ;
						openDataDeliveries.removeItem(ix) ;
						
						// mb\transactData\source
						
						bufferfilename = msgBoardStorage.createTransactionBufferfileName( instanceID,  transactionID, "SOURCE", "SOURCE") ;
						if (fileutil.fileexists(bufferfilename)){
							fileutil.deleteFile(bufferfilename) ;
						}
						
						if (transaction != null){
							transaction.setTransactState(Transaction.TS_RESULTS_RETURNED);

							bufferfilename = msgBoardStorage.createTransactionBufferfileName( instanceID,transactionID, "SOURCE", "SOURCE", 2);
							msgBoardStorage.storeTransactionDataObj(resultPackage,bufferfilename);
							//
							resultsReceiver.performTask(resultPackage);
						}
						return true;
					}
					
					
					long tid = resultPackage.getTransactionID() ;
					
					if (tid<=0){
						resultPackage.setTransactionID(transactionID) ;
					}
					
					out.print(3, "trying to confirm transaction id="+transactionID+" (state:"+transactState+") to MessageBoard ...");
					 
					r=0;
					
					if (r==0){
						confirmationSent = true;
												out.print(3, "confirmation sent successfully to MessageBoard...");	
						// upon receiving results, this must be removed from this collection again
						// TODO: 
						opentransactions = mbf.getOpenTransactions();
						
						if (opentransactions.containsKey(transactionID)){
							// checking the secret?
							// mappedSecret = opentransactions.get(transactionID) ;
							opentransactions.remove(transactionID ) ; 
							
							
							// upon change, save
							msgBoardStorage.saveParticipantstransactions( str,"SOURCE", "open", opentransactions );
							
						}

						// not removing, but closing
						ix = transactions.getIndexByTransactionID(transactionID, 1) ;
						transaction = transactions.getItem(ix) ;
						transaction.setTransactState( Transaction.TS_RESULTS_RETURNED ) ;
						transactions.save() ;
						
						// note, that we have been updated as an Observer, so we have to route this further up the hierarchy !
						
						
						
												out.print(3,"SOURCE (id:"+instanceID+") sending confirmation about transaction (tid:"+transactionID+")") ;
						r = confirmToMessageBoard( xMessage, transactionID, instanceID, transaction.getTransactState(), secret, "SOURCE",0);
						// how does the message look like?					 

						// this refers to openTransactions
						mbf.removeTransactionFromList(transactionID);

						
												out.print(3,"SOURCE (id:"+instanceID+") going for assimilation of results...");
							
						resultPackage.setTypeOfProceedings("results");
						// MessageBoardParticipant::MessageReceptorDigester::performTask()
						resultsReceiver.performTask(resultPackage) ;
						// resultsReceiver links back to MessageReceptorDigester, which
						// implements ReceptorDigestIntf, as an inner class of "MessageBoardParticipant"
						
						
					}
					
				}catch(Exception e){
					result = false;
					out.printErr(2, "decoding the result object failed for tansaction id="+transactionID+" !");
					e.printStackTrace() ;
				}
				
				// send confirmation back to MessageBoard, which causes the board
				// to close (and possibly archive) the transaction
				 
				
				if (resultPackage!=null){
					result = true;
				}
				
			
		}
		
			
		return result;
	}
	
	private boolean replyVariant_HandlingErrorMsgOnResultsRequest( XMessage xMessage, String rawXmlStr , String[]rawIndicators){
		
		boolean result = false;
		
		String secret, stateDescr, includedErrMsg;
		int sv,errcode;
		long transactionState;
		long tid, ec;
		Transaction transaction ;
		TaskPackage taskPackage;
		
		result = rawIndicatorsCheck( rawXmlStr, rawIndicators);
		
		if (result){
			
			// long iid = xMessage.getinstanceID(rawXmlStr, "instanceID");
			tid = xMessage.getTransactionID(rawXmlStr) ;
			
			transactionState = xMessage.getSpecifiedValue(rawXmlStr, "transactionID", "state") ;
			sv = (int)transactionState;
			stateDescr = Transaction.decodeTransactionCode(sv) ;
			
			includedErrMsg = xMessage.readReturnedResults(rawXmlStr) ;
			
			// ec is -1 , should be -20 ??
			ec = xMessage.getSpecifiedValue(rawXmlStr, "//result", "value") ; // return
			errcode = (int)ec;
			
			if (errcode==0){
				secret =  xMessage.getSpecifiedInfo(rawXmlStr, "transactionID", "secret") ;
												out.print(3, "State message received for transaction id="+tid+", state of transaction : "+transactionState+" = "+stateDescr) ;

				transaction = transactions.getTransactionByID(tid) ;
				
			}
			if (errcode!=0){

				if ((transactionState==Transaction.TS_TASK_CANCELED) && (xRegistration.getTasksBeingCanceled().indexOf(tid)>=0)){
					// it is a canceled task, and we know that we caused it...
					out.print(2, "Cancelation of transaction (tid:"+tid+") has been confirmed.");
					// remove it from MessageBoard
					// ... it should be removed...
					transaction = transactions.getTransactionByID(tid) ;
					transaction.setTransactState( (int)transactionState);
					transactions.save();
					return true;
				}
				secret =  xMessage.getSpecifiedInfo(rawXmlStr, "transactionID", "secret") ;
												out.printErr(2, "Error message received for transaction id="+tid+", state of transaction : "+transactionState+" = "+stateDescr) ;
												out.printErr(2, "Error code = "+errcode+"   \n\r"+includedErrMsg);
			
				transaction = transactions.getTransactionByID(tid) ;
		    
				if (transaction!=null){
					if (transaction.getClientSecret().contentEquals(secret)==false){
						out.print(2, "faulty transaction can't be removed, since client secret don't match!");
					}else{
				
						// also : remove buffer file from outgoing transaction
						// 		  notify client on frontend !!!
						// creating an artificial task package, containing "error" as the data object string
				
						prepareErrorPackageForClient( transaction, tid, sv,errcode, includedErrMsg);
						// remove notifications !!!
				 
					}
				}else{
					// tid is unknown anyway, so nothing to do...
				}
			}
		}

		
	
		return result;
	}
	
	
	private void prepareStatePackageForClient( Transaction transaction, long transactionID, 
			   								   int statevalue, int errcode, String includedStateMsg){
		
		TaskPackage taskPackage;
		
		taskPackage = new TaskPackage();
	
		taskPackage.setDataObj(transaction);

		taskPackage.setTransactionID(transactionID);
		
		if (errcode==0){
			errcode = statevalue;
		}
		taskPackage.setDataString("state code:" + errcode + "(" + Transaction.decodeTransactionCode(errcode) + ")");
		taskPackage.setContentType("result");

		taskPackage.setTypeOfProceedings("stateInformation");

		if (transaction==null){
			return;
		} 
		
		 
		transaction.setTransactState(statevalue);
		transaction.setDataObject("");

		transactions.save();

		transaction.setDataObject(includedStateMsg);
		taskPackage.setTransactionMirror(transaction);

		resultsReceiver.performTask(taskPackage);
 
		// transactions.save();
		 
	}
	
	
	private void prepareErrorPackageForClient( Transaction transaction, long transactionID, 
			   								   int statevalue, int errcode, String includedErrMsg){
		
		TaskPackage taskPackage;
		
		
		
		taskPackage = new TaskPackage();
		taskPackage.setDataObj(transaction);

		taskPackage.setTransactionID(transactionID);
		taskPackage.setDataString("error, code:" + errcode + "(" + Transaction.decodeTransactionCode(errcode) + ")");
		taskPackage.setContentType("result");

		taskPackage.setTypeOfProceedings("failedTransaction");

		if (transaction==null){
			transaction = new Transaction(); // marker for NEW T
		} 
		
		transaction.setTransactErrorState(errcode);
		transaction.setTransactState(statevalue);
		transaction.setDataObject(includedErrMsg);

		transactions.save();

		taskPackage.setTransactionMirror(transaction);

		resultsReceiver.performTask(taskPackage);

		transactions.removeItem(transaction);
		transactions.save();
		
		OpenTransactions ots ;
		SortedMap<Long, String> ots_map;
		ots_map = mbf.getOpenTransactions() ;
		
		
		OpenDataDeliveries opdd = mbf.getOpenDataDeliveries() ;
		int ix = opdd.indexOfItem(transactionID) ;
		if (ix>=0){
			opdd.removeItem(ix) ;
		}
		 
		
	}
	
	
	
	private boolean replyVariant_HandlingStateMessage( XMessage xMessage, String rawXmlStr , String[]rawIndicators){
		boolean result = false;
		
		result = rawIndicatorsCheck( rawXmlStr, rawIndicators);
		
		if (result){
			// result = false;
									out.print(2,"replyVariant_HandlingStateMessage()...");
		
		}
		
		
		return result;
	}
	
	/**
	 * 
	 * this is also triggered by messages, which have been composed by the MessageBoard itself,
	 * see: handlingBlockedTransactions() <- SourceMessageInterpreter , using signature : rawIndicators = new String[]{"<request ","type=\"delivery\""} ;
	 * e.g. in case of critical states of the message board or the transaction (no ClientMatch -> findMatchingParticipant...)
	 * 
	 * @param xMessage
	 * @param rawXmlStr
	 * @param rawIndicators
	 * @return
	 */
	private boolean replyVariant_NotificationAboutTransaction( XMessage xMessage, String rawXmlStr , String[]rawIndicators){
		/*
		 	<messageboard name="spela" role="BOARD">
    			<transaction>
       				<notification description="No matching participant found." type="state" value="11"/>
       				<transaction id="226" secret="93e73170-f38f-11e0-913f-e0f847024516"/>
    			</transaction>
 			</messageboard>
		 */
		boolean  result = false;
		String str,tsworded="",statedescription="" ,secretstr="", bufferfilename;
		int tscode = -9;
		long transactionID ;
		
		
		Transaction transaction=null;
		TaskPackage taskPackage = new TaskPackage();
		
		result = rawIndicatorsCheck( rawXmlStr, rawIndicators);
		
		if (result){
			
			str = xMessage.getNotificationInfo(rawXmlStr, "type");
			
			if (str.contentEquals("state")){
				tscode = xMessage.getNotificationStateValue(rawXmlStr, "value", -1);
				tsworded = Transaction.decodeTransactionCode(tscode) ;
				statedescription = xMessage.getNotificationInfo(rawXmlStr, "description");
			
			}
			
			transactionID = xMessage.getTransactionID(rawXmlStr) ;
			
			if (transactions!=null){
				transaction = transactions.getTransactionByID( transactionID ) ;
			}
			
			if (transaction==null){
				transaction = xMessage.readConfirmedTransactionReceipt(rawXmlStr) ;
				transaction.setTransactionID(transactionID) ;
				transaction.setRoleOfInitializer("SOURCE") ;
			}
			if (tscode==Transaction.TS_TASK_DENIED){
				// get requested doc type
				
			}
												out.printErr(2, "Critical state information (code:"+tscode+" = "+ tsworded +")"+
														        " about transaction "+transactionID+": "+statedescription+".");
												out.print(4, rawXmlStr+"\n\r\n-------------------------------------\n\r\n");
			if (transaction!=null){
				
				// get the filename
				bufferfilename = msgBoardStorage.createTransactionBufferfileName( transaction, "SOURCE" ) ;
				
				if (fileutil.fileexists(bufferfilename)){
					transaction = (Transaction)msgBoardStorage.readTransactionDataObj(bufferfilename) ;
				}
				
				secretstr = xMessage.getSpecifiedInfo(rawXmlStr, "transaction", "secret");
				// we read the file , which we have stored
				transaction.setClientSecret(secretstr) ;
				transaction.setTransactState(tscode) ;
				
				taskPackage.setTypeOfProceedings("failedTransaction") ;
				taskPackage.setData( transaction, "transaction" );
				taskPackage.setContentType("transaction") ;
				
				resultsReceiver.performTask(taskPackage) ;
				
				// the SOURCE will treat this first as a normal task, until it discovers
				// the particular value in the field "typeOfProceedings" of the taskpackage
				
			}// transaction found?
			
		}
			
		return result ;
	}

		
	
	private boolean handling_NotificationAboutConfirmedConnection( XMessage xMessage, String rawXmlStr , String[]rawIndicators){
		/*
		 <messageboard name="spela" role="BOARD">
   			<transaction>
      			<register>
        			<notification type="connection" value="1"/>
         			<name value="testsource0-source-1-7132c490-f138-11e0-84ce-e0f847024516"/>
      			</register>
   			</transaction>
		 </messageboard>
		  
		 */
		boolean hb=false, result = false;
		
		
												 
		result = rawIndicatorsCheck( rawXmlStr, rawIndicators);
		
		if (result){
			
			hb = xMessage.checkConnectionConfirmationMatch ( rawXmlStr, xRegistration.getInstanceID(), xRegistration.getNameOfApplicant());
			 
			if (hb){
				// set xreg's connection finally to active, 
				// server-side, this is important for the client-matcher
				xRegistration.setInstanceConnectionConfirmed(true);
				out.print(3, "Instance is now connected to MessageBoard, and connection has been confirmed.");
			}else{
				out.print(2, "Instance (type:source) tried to connect to MessageBoard, but connection has not been confirmed.");
				out.print(4, rawXmlStr);
			}
		}
		 
		return result ;
	}
	
	 
	
	private boolean handling_ConfirmationAboutTaskRequest( XMessage xMessage, String rawXmlStr , String[]rawIndicators){
	/*
			<messageboard name="spela" role="BOARD">
   				<transaction>
      				<transaction id="-9"/>
      				<confirmation secret="testSource0-source-1-4a3871c9-f687-40b4-8d2d-53ec78b5f00b" type="task::cancel-all">
      				-> TODO: <param value="1;2;3;4;5" unknown="6;7"> ...an id or a list of 
      				</confirmation>
      				<instance id="2528"/>
   				</transaction>
			</messageboard>
	*/
		boolean hb=false, result = false;
		long tid;
		String str="",noncstr="", secret;
		 
		result = rawIndicatorsCheck( rawXmlStr, rawIndicators);
		
		 
		if (result){
			secret = xmsg.getSpecifiedInfo(rawXmlStr, "//transaction/confirmation", "secret");
										out.printErr(3, ">>>  handling_ConfirmationAboutTaskRequest()");
			// call back to the sender which is waiting, 
			// TODO: should use the transaction secret, and a list of senders in order to avoid collisions
			informSender.informAboutConfirmation(true, secret);
			
			str= xmsg.getSpecifiedInfo(rawXmlStr, "param", "value");
			
			if (rawXmlStr.contains("task::cancel-all")){
				// str should be "all"
				resultsReceiver.performSystemTask("cancel::all");		
				return true;
			}
			// if it is not "cancel-all", but still canceling a specific one 
			if (rawXmlStr.contains("task::cancel")){
				// str should contain the id or a list of ids
				noncstr= xmsg.getSpecifiedInfo(rawXmlStr, "param", "unknown");
				resultsReceiver.performSystemTask("cancel::id="+str);
				if (noncstr.length()>0){
					resultsReceiver.performSystemTask("noncancel::id="+noncstr);
				}
			}
			
		}
		
		
		return result ;
	}
	
	
	private boolean handling_ConfirmationAboutCancelTask( XMessage xmsg, String rawXmlStr , String[]rawIndicators){
	/*
	 		<messageboard name="spela" role="BOARD">
   				<transaction>
      				<transaction id="-9" secret="5d38a05b-1c4e-4210-9498-381fe480effd"/>
      				<control type="cancel-all"/>
      				<confirmation success="" unknown="all"/>
      				<instance id="2528"/>
   				</transaction>
			</messageboard>
	 */
		boolean hb=false, result = false;
		long iid,tid;
		String str, secret, unknownsStr, successStr;
		 
		result = rawIndicatorsCheck( rawXmlStr, rawIndicators);
		
		if (result){
			
			secret = xmsg.getSpecifiedInfo(rawXmlStr, "//transaction/transaction", "secret");
			// callback to confirmedSender  
			informSender.informAboutConfirmation(true, secret);
			
												out.printErr(3, ">>>  handling_ConfirmationAboutCancelTaskXMessage()");
												out.printErr(4, ""+rawXmlStr);
			
			tid = xmsg.getSpecifiedValue(rawXmlStr, "//transaction/transaction", "id");
			iid = xmsg.getSpecifiedValue(rawXmlStr, "//transaction/instance", "id");
			// is it indeed for us ?
			
			if (xRegistration!=null){
				
			}
			str = xmsg.getSpecifiedInfo(rawXmlStr, "control", "type"); // "cancel", or "cancel-all" 
			
			successStr  = xmsg.getSpecifiedInfo(rawXmlStr, "confirmation", "success"); //  a list "1;2;3"
			unknownsStr = xmsg.getSpecifiedInfo(rawXmlStr, "confirmation", "unknown"); //  a list "1;2;3" or "all"
			
			// send a pause for 1 cycle to the uplink result checker: as a system task 
			
			
			
			// we remove all transactions for which we have a success or a unknown
			if (unknownsStr.contentEquals("all")){
				// system task: remove/cancel all
				resultsReceiver.performSystemTask( "cancel::all") ;
			}else{
				String[] unknowns = unknownsStr.split(";");
				
				for (int i=0;i<unknowns.length;i++){
					str = unknowns[i] ;
					// send system task
					if (str.length()>0){
						resultsReceiver.performSystemTask( "cancel::id="+str) ;
					}
					delay(5);
				}
			}
			
			// splitting the system task per transaction id !
			String[] success = successStr.split(";");
			for (int i=0;i<success.length;i++){
				str = success[i] ;
				// send system task
				if (str.length()>0){
					resultsReceiver.performSystemTask( "cancel::id="+str) ;
				}
				delay(3);
			}
		}
		return result ;
	}
	
	
	private boolean handling_NotificationAboutControlTask( XMessage xMessage, String rawXmlStr , String[]rawIndicators){
	
		boolean hb=false, result = false;
		long tid;
		String secret;
		 
		result = rawIndicatorsCheck( rawXmlStr, rawIndicators);
		
		if (result){
			
												out.printErr(5, ">>>  handling_NotificationAboutControlTask()");
			secret = xmsg.getSpecifiedInfo(rawXmlStr, "//transaction/transaction", "secret");
			hb = true ;
				// xMessage.checkConnectionConfirmationMatch ( rawXmlStr, xRegistration.getInstanceID(), xRegistration.getNameOfApplicant());
				// getSecretOfTransaction( long transactionID)
			
			if (informSender!=null){
				informSender.informAboutConfirmation( true , secret) ;
			}

			tid = xMessage.getTransactionID(rawXmlStr) ;
			if (hb){
				out.print(2, "Control task for transaction (tid:"+tid+") has been confirmed."); //, transaction (tid:"+tid+") has been canceled.");
			}else{
				out.print(2, "Control task transaction (tid:"+tid+") has NOT been confirmed.");
			}
			result =true;
		}
		 
		return result ;
	}

	
	
	private boolean handling_deniedTransaction( XMessage xMessage, String rawXmlStr , String[]rawIndicators){
		
		boolean result = false, hb=false;
		String str, stateValStr , name, secret;
		long iid, tid;
		int statecode;
		XRegistration xreg;
		Transaction transaction;
		
		result = rawIndicatorsCheck( rawXmlStr, rawIndicators);
		
		if (result){

			statecode = (int) xMessage.getSpecifiedValue(rawXmlStr, "state", "code") ;
			
			stateValStr = xMessage.getSpecifiedInfo(rawXmlStr, "state", "value") ;
			
			tid = xMessage.getSpecifiedValue(rawXmlStr, "transaction", "id") ;
			
			iid = xMessage.getSpecifiedValue(rawXmlStr, "instanceID", "value") ;
			
			secret = xMessage.getSpecifiedInfo(rawXmlStr, "transaction", "secret") ;
			
			name = xMessage.getSpecifiedInfo(rawXmlStr, "name", "value") ;
			
			xreg = mbf.getRegisteredInstances().get(0);
			transaction = mbf.getTransactions().getTransactionByID(tid);
			
			if (transaction!=null){
				hb = transaction.getClientSecret().contentEquals(secret) ;
			}
			if (hb){
				name=name+"" ;
			}
			if ((statecode<0) && ( xreg.getNameOfApplicant().contentEquals(name))){
				taskPackage = new TaskPackage();
				
				taskPackage.setTypeOfProceedings("failedTransaction");
				taskPackage.setDataString("cancel::"+stateValStr);

				taskPackage.setTransactionID( tid ) ;
				taskPackage.setStateFlag( statecode ) ;
				
				taskPackage.setData(transaction, stateValStr) ;
				resultsReceiver.performTask(taskPackage) ;
			}
			
		}
		return result ;
	}
		
	
	private boolean handling_DigestingNotification( XMessage xMessage, String rawXmlStr , String[]rawIndicators){
		boolean result = false;
		long transactionID;
		int transactState = -1; 
		String description = "";
		Transaction transaction=null;
		
		String str;
		/*
		 	<messageboard name="spela" role="RECEPTOR">
    			<transaction>
       				<notification type="state" value="task completion : 47.0%"/>
       				<transaction id="138" secret="fe9e6f00-f2a0-11e0-bbce-e0f847024516"/>
    			</transaction>
 			</messageboard>
		  
		 */
		result = rawIndicatorsCheck( rawXmlStr, rawIndicators);
		
		if (result){
			 
			
			transactionID = xMessage.getTransactionID(rawXmlStr) ;
			
			if (transactions!=null){
				// TODO: not properly created for SOURCE...
				transaction = transactions.getTransactionByID(transactionID) ;
			}
			
			str = xMessage.getSpecifiedInfo(rawXmlStr, "notification", "value") ;
			  	  if (strgutil.isNumericX(str)){
			  		  transactState = Integer.parseInt(str) ;
			  	  }
			  	  
			str = xMessage.getSpecifiedInfo(rawXmlStr, "notification", "description") ;
				  description = str;
				  
			str = xMessage.getSpecifiedInfo(rawXmlStr, "transaction", "secret") ;
			
			out.print(5,"--------   notification received by source for transaction ("+transactionID+") : "+description+" ...");
			
			if (transaction!=null){
				if (transactState >= 0) {
					transaction.setTransactState(transactState);
					transactions.stateChangedFor(transactionID);
				}
				if (description.length() > 0) {
					transaction.setTransactStateDescription(description);
					transactions.stateChangedFor(transactionID);
				}
			}
			
			resultsReceiver.performSystemTask("state::tid="+transactionID+"::"+description);
			
		}
		
		return result; 
	}
	
	
	
	public int rawAcceptanceCheck(String rawXmlStr){
		int result = -1;
		


		int p3=-1;
		xmsg = new XMessage(out) ;
		
	
	
		// ensure that this transaction receipt is indeed for us! The instance has provided its private port, but we check !
		 
		result = 0;
		
		// name of this instance?
		p3 = rawXmlStr.indexOf( this.nameOfInstance );
		
		
		// is it a transaction?
		if (p3<0){
			p3 = rawXmlStr.indexOf( "transactionID" );// +" value=\""
			if (p3<0){
				p3 = rawXmlStr.indexOf("transaction id=\"");
			}
		}
		
		// is it a notification?
		if (p3<0){
			p3 = rawXmlStr.indexOf( "<notification" );// +" value=\""
		}
		if (p3<0){
			p3 = rawXmlStr.indexOf( "<name value=\"*\"" ); // would be an anonymous ("broadcasted") message
			if (p3<0){
				result = -402;
				
			}
		}
		return result;
	}
	
	
	// ------------------------------------------------------------------------
	
	private boolean rawIndicatorsCheck( String rawXmlMsg, String[] rawIndicators){
		boolean rB=false;
		int isum=0;
		
		int[] p = new int[rawIndicators.length] ;
		
		for (int i=0;i<rawIndicators.length;i++){
			p[i] = rawXmlMsg.indexOf( rawIndicators[i]);
			
			if (p[i]>=0){
				isum = isum+ 1;
			}
		}
		
		rB = (isum == p.length) ;
		
		return rB;
	}


	private boolean rawOptionalIndicatorsCheck( String rawXmlMsg, String[] rawOptIndicators, int miniMatch){
		boolean rB=false;
		int isum=0;
		
		int[] p = new int[rawOptIndicators.length] ;
		
		for (int i=0;i<rawOptIndicators.length;i++){
			p[i] = rawXmlMsg.indexOf( rawOptIndicators[i]);
			
			if (p[i]>=0){
				isum = isum+ 1;
			}
		}
		
		rB = (isum >= miniMatch ) ;
		
		return rB;
	}
	
	
	private int confirmToMessageBoard( XMessage xMessage, long transactionID, long instanceID, int transactionState, String secret, String role , int validity){
		String xmlstr, xmlRestPrefix;
		int result = -1;
	
		
		xmlstr = xmsg.createConfirmationAboutReceivedResults( transactionID, instanceID, transactionState, secret, "SOURCE" );
		
		if (validity<0){
			
			xmlstr = xmsg.changeAddSegment( xmlstr, "//transaction", "confirmation", "action", "remove") ;
		} 
		
						out.print(3, "instance " + instanceID + " (SOURCE) is sending (port:" + msgBoardPort+") "+
									 "a confirmation to message board about transaction " + transactionID + "...    ");
		 
						
		    if (mbf.getFactoryProperties().getProtocolId() <= MessageBoardFactoryProperties.protocolFTP){
		    	xmlRestPrefix = xMessage.createRestletRoutingPrefix("", "/data", "post");
		    	xmlstr = xmlRestPrefix + xmlstr;
		    	
		    	 
		    }else{
		    	
		    }
		
			// add no wait
			// if (waitforConfirmation<=0)
			if (mbf.getFactoryProperties().getProtocolId() <= MessageBoardFactoryProperties.protocolFTP){
				// this is an advice for the http-sender... we need not to wait for a confirmation of an confirmation
				String str="";
				str = "<waitforresponse value=\"0\" />\r\n   ";
				xmlstr = xmlstr.replace("</message", str+"</message");
			}
		
						out.print(3, "Confirmation to port "+msgBoardPort+" by source in confirmToMessageBoard() ...");
						out.print(4, xmlstr);
						
		// this is not a reply, it is active, i.e. in case of http it is a post 
		sender = new SenderWrap(comBox);
		sender.sendingMessage( xmlstr , msgBoardPort ) ;
		sender=null;
		
		result = 0;
		
		return result ;
	}
	
	 
	private String introduceFiltertoMsgFrame( String xmlstr, String replacementStr){
		
		//   <!--  "##DATA##"   -->
		int pd,pc,pe ;
		String cstr;
		
		pd = xmlstr.indexOf( "##FILTER##" ) ;
		pc = strgutil.indexOfBefore( xmlstr, "<!--", pd);
		pe = xmlstr.indexOf("-->", pc)+3;
		
		cstr = xmlstr.substring(pc, pe) ;
		
		xmlstr = xmlstr.replace( cstr, "  ");
		
		xmlstr = strgutil.insert( xmlstr, replacementStr, pc);
		
		
		return xmlstr;
	}

	@SuppressWarnings({ "static-access"  })
	private void delay( int millis ){
	
		try {
			Thread.currentThread().sleep(millis);
		} catch (InterruptedException e) {
		}
	}
	
	private String introduceDatatoMsgFrame( String xmlstr, String replacementStr){
		
		//   <!--  "##DATA##"   -->
		int pd,pc,pe ;
		String cstr;
		
		pd = xmlstr.indexOf( "##DATA##" ) ;
		pc = strgutil.indexOfBefore( xmlstr, "<!--", pd);
		pe = xmlstr.indexOf("-->", pc)+3;
		
		cstr = xmlstr.substring(pc, pe) ;
		
		xmlstr = xmlstr.replace( cstr, "  ");
		
		xmlstr = strgutil.insert( xmlstr, replacementStr, pc);
		
		
		return xmlstr;
	}

	public void setResourceLocator( String resourceloc){
		resourceLocator = resourceloc ;
		
	}

	public void setContentType(String contenttype) {
		this.contentType = contenttype;
	}

	 
	public void setMappedPackagesCollection( Map<String, Object> mappedpackages) {
		 
		mappedPackagesCollection = mappedpackages;
	}
	
}
